package com.owg.jigsaw.javafx;

import java.util.Arrays;
import java.util.List;

import com.owg.jigsaw.WPointerBlock;
import com.owg.jigsaw.contour.Contour;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.transform.Transform;

public class FXArrow implements ChangeListener<Transform>
{
	private static final double REF_BLK_OFFSET = FXBlock.INDENTATION_OFFSET;
	
	public final WPointerBlock pointerBlock;
	InvalidationListener visibilityListener;
	
	private final Line line;
	private final Line leftHead;
	private final Line rightHead;

	public FXArrow(WPointerBlock p)
	{
		super();
		pointerBlock = p;
		Pane pBlkUI = p.getUI();
		Pane refUI = pointerBlock.referenceBlock.getUI();
		pBlkUI.localToSceneTransformProperty().addListener(this);
		refUI.localToSceneTransformProperty().addListener(this);
		visibilityListener = new InvalidationListener()
		{
			@Override
			public void invalidated(Observable observable)
			{
				updateVisibility();
			}
		};
		FXUtil.addVisibilityListenerTo(pBlkUI, new WeakInvalidationListener(visibilityListener));
		FXUtil.addVisibilityListenerTo(refUI, new WeakInvalidationListener(visibilityListener));
		line = new Line();
		leftHead = new Line();
		rightHead = new Line();
		updateLine();
	}

	public List<Node> getNodes()
	{
		return Arrays.asList(new Node[]{line, leftHead, rightHead});
	}	

	@Override
	public void changed(ObservableValue<? extends Transform> prop,
		Transform oldVal, Transform newVal)
	{
		updateLine();
	}

	private void updateLine()
	{
		Pane pBlkUI = pointerBlock.getUI();
		Pane refUI = pointerBlock.referenceBlock.getUI();
		Pane rootUI = pointerBlock.getRoot().getUI();
		
		final Contour contour = pointerBlock.tabType.get().getContour();
		final int tabSize = Math.max(contour.getWidth(), contour.getHeight());
		final int pBlkOffset = FXBlock.TAB_OFFSET + tabSize/2;
		
		Point2D startPoint = FXUtil.fromTo(pBlkUI, rootUI, new Point2D(pBlkOffset, pBlkOffset));
		line.startXProperty().set(startPoint.getX());
		line.startYProperty().set(startPoint.getY());
		Point2D endPoint = FXUtil.fromTo(refUI, rootUI, new Point2D(REF_BLK_OFFSET, REF_BLK_OFFSET));
		line.endXProperty().set(endPoint.getX());
		line.endYProperty().set(endPoint.getY());
		
		Point2D midPoint = endPoint.add(startPoint).multiply(0.5);
		Point2D direction = endPoint.subtract(startPoint).normalize();
		Point2D perp = new Point2D(-direction.getY(), direction.getX());
		final int arrowSize = 8;
		final int arrowWidth = 8;
		
		leftHead.startXProperty().set(midPoint.getX()+direction.getX()*arrowSize);
		leftHead.startYProperty().set(midPoint.getY()+direction.getY()*arrowSize);
		leftHead.endXProperty().set(midPoint.getX()-direction.getX()*arrowSize+perp.getX()*arrowWidth);
		leftHead.endYProperty().set(midPoint.getY()-direction.getY()*arrowSize+perp.getY()*arrowWidth);
		
		rightHead.startXProperty().set(midPoint.getX()+direction.getX()*arrowSize);
		rightHead.startYProperty().set(midPoint.getY()+direction.getY()*arrowSize);
		rightHead.endXProperty().set(midPoint.getX()-direction.getX()*arrowSize-perp.getX()*arrowWidth);
		rightHead.endYProperty().set(midPoint.getY()-direction.getY()*arrowSize-perp.getY()*arrowWidth);
		
		updateVisibility();
	}

	private void updateVisibility()
	{
		Pane pBlkUI = pointerBlock.getUI();
		Pane refUI = pointerBlock.referenceBlock.getUI();
		boolean v = FXUtil.isVisible(pBlkUI) && FXUtil.isVisible(refUI);
		for(Node n : getNodes())
		{
			n.setVisible(v);
		}
	}
}
